/**
 * @author      : alpha (alpha@mascot)
 * @created     : Sunday Mar 13, 2022 17:38:30 CST
 */

#include <linux/module.h>

static char          *param_charp   = "param_charp";
static int            param_int     = 1;
static long           param_long    = 2;
static short          param_short   = 3;
static unsigned int   param_uint    = 4;
static unsigned long  param_ulong   = 5;
static unsigned short param_ushort  = 6;
static bool           param_bool    = 7;
static bool           param_invbool = 8;
module_param(param_charp,   charp,   0644);
module_param(param_int,     int,     0644);
module_param(param_long,    long,    0644);
module_param(param_short,   short,   0644);
module_param(param_uint,    uint,    0644);
module_param(param_ulong,   ulong,   0644);
module_param(param_ushort,  ushort,  0644);
module_param(param_bool,    bool,    0644);
module_param(param_invbool, invbool, 0644);

static int param_int_array[] = {1, 2, 3, 4, 5, 6, 7, 8};
module_param_array(param_int_array, int, NULL, 0644);
static char param_string[256] = {"param_string"};
module_param_string(param_string, param_string, sizeof(param_string), 0644);

static int param_int_cb = 9;
int set_param_int_cb(const char *val, const struct kernel_param *kp)
{
	int old = param_int_cb;
	int ret = param_set_int(val, kp);

	if(ret == 0) {
		printk(KERN_INFO "set param_int_cb %d -> %d success: %s\n",
			old, param_int_cb, val);
	} else {
		printk(KERN_INFO "set param_int_cb(%d == %d) failed: %s\n",
			old, param_int_cb, val);
	}

	if (param_int_cb < 0 || param_int_cb > 1024) {
		printk(KERN_INFO "set param_int_cb(%d) "
			"out of range[%d, %d], reset to %d: %s\n",
			param_int_cb, 0, 1024, old, val);
		param_int_cb = old;
	}

	return ret;
}
const struct kernel_param_ops param_int_cb_ops = {
        .set = set_param_int_cb,
        .get = param_get_int,
};
module_param_cb(param_int_cb, &param_int_cb_ops, &param_int_cb, 0644);

static long param_long_cb = 10;
int set_param_long_cb(const char *val, const struct kernel_param *kp)
{
	long n = 0;
	int ret;

	ret = kstrtol(val, 0, &n);
	if (ret != 0 || n < 1 || n > 32) {
		printk("set param_long_cb(%ld) failed: %s", n, val);
		return -EINVAL;
	}
	return param_set_long(val, kp);
}
const struct kernel_param_ops param_long_cb_ops = {
        .set = set_param_long_cb,
        .get = param_get_long,
};
module_param_cb(param_long_cb, &param_long_cb_ops, &param_long_cb, 0644);

static void print_all_param(void)
{
	size_t i;

	printk("param_charp   = '%s'\n", param_charp);
	printk("param_string  = '%s'\n", param_string);
	printk("param_int     = %d\n",   param_int);
	printk("param_long    = %ld\n",  param_long);
	printk("param_short   = %hd\n",  param_short);
	printk("param_uint    = %u\n",   param_uint);
	printk("param_ulong   = %lu\n",  param_ulong);
	printk("param_ushort  = %hu\n",  param_ushort);
	printk("param_bool    = %d\n",   param_bool);
	printk("param_invbool = %d\n",   param_invbool);
	printk("param_int_cb  = %d\n",   param_int_cb);
	printk("param_long_cb = %ld\n",  param_long_cb);

	printk("param_int_array = {");
	for (i = 0; i < ARRAY_SIZE(param_int_array); i++) {
		printk("%d,", param_int_array[i]);
	}
	printk("}\n");
}

static int hello_module_init(void)
{
	printk(KERN_INFO "[%s:%d] Hello, Module!\n", __func__, __LINE__);
	print_all_param();
	return 0;
}

static void hello_module_exit(void)
{
	print_all_param();
	printk(KERN_INFO "[%s:%d] Hello, Module!\n", __func__, __LINE__);
}

module_init(hello_module_init);
module_exit(hello_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("alpha@mascot.x");
MODULE_DESCRIPTION("Hello, Module!");
